1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect.testing.google;
18  
19  import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
20  import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION;
21  import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS;
22  import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD;
23  import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
24  import static com.google.common.collect.testing.features.CollectionSize.SEVERAL;
25  import static com.google.common.collect.testing.features.CollectionSize.ZERO;
26  
27  import com.google.common.annotations.GwtCompatible;
28  import com.google.common.collect.Multiset;
29  import com.google.common.collect.Multiset.Entry;
30  import com.google.common.collect.testing.features.CollectionFeature;
31  import com.google.common.collect.testing.features.CollectionSize;
32  
33  import java.util.ConcurrentModificationException;
34  import java.util.Iterator;
35  
36  /**
37   * Common superclass for {@link MultisetSetCountUnconditionallyTester} and
38   * {@link MultisetSetCountConditionallyTester}. It is used by those testers to
39   * test calls to the unconditional {@code setCount()} method and calls to the
40   * conditional {@code setCount()} method when the expected present count is
41   * correct.
42   *
43   * @author Chris Povirk
44   */
45  @GwtCompatible(emulated = true)
46  public abstract class AbstractMultisetSetCountTester<E>
47      extends AbstractMultisetTester<E> {
48    /*
49     * TODO: consider adding MultisetFeatures.SUPPORTS_SET_COUNT. Currently we
50     * assume that using setCount() to increase the count is permitted iff add()
51     * is permitted and similarly for decrease/remove(). We assume that a
52     * setCount() no-op is permitted if either add() or remove() is permitted,
53     * though we also allow it to "succeed" if neither is permitted.
54     */
55  
56    private void assertSetCount(E element, int count) {
57      setCountCheckReturnValue(element, count);
58  
59      assertEquals(
60          "multiset.count() should return the value passed to setCount()",
61          count, getMultiset().count(element));
62  
63      int size = 0;
64      for (Multiset.Entry<E> entry : getMultiset().entrySet()) {
65        size += entry.getCount();
66      }
67      assertEquals(
68          "multiset.size() should be the sum of the counts of all entries",
69          size, getMultiset().size());
70    }
71  
72    /**
73     * Call the {@code setCount()} method under test, and check its return value.
74     */
75    abstract void setCountCheckReturnValue(E element, int count);
76  
77    /**
78     * Call the {@code setCount()} method under test, but do not check its return
79     * value. Callers should use this method over
80     * {@link #setCountCheckReturnValue(Object, int)} when they expect
81     * {@code setCount()} to throw an exception, as checking the return value
82     * could produce an incorrect error message like
83     * "setCount() should return the original count" instead of the message passed
84     * to a later invocation of {@code fail()}, like "setCount should throw
85     * UnsupportedOperationException."
86     */
87    abstract void setCountNoCheckReturnValue(E element, int count);
88  
89    private void assertSetCountIncreasingFailure(E element, int count) {
90      try {
91        setCountNoCheckReturnValue(element, count);
92        fail("a call to multiset.setCount() to increase an element's count "
93            + "should throw");
94      } catch (UnsupportedOperationException expected) {
95      }
96    }
97  
98    private void assertSetCountDecreasingFailure(E element, int count) {
99      try {
100       setCountNoCheckReturnValue(element, count);
101       fail("a call to multiset.setCount() to decrease an element's count "
102           + "should throw");
103     } catch (UnsupportedOperationException expected) {
104     }
105   }
106 
107   // Unconditional setCount no-ops.
108 
109   private void assertZeroToZero() {
110     assertSetCount(samples.e3, 0);
111   }
112 
113   private void assertOneToOne() {
114     assertSetCount(samples.e0, 1);
115   }
116 
117   private void assertThreeToThree() {
118     initThreeCopies();
119     assertSetCount(samples.e0, 3);
120   }
121 
122   @CollectionFeature.Require(SUPPORTS_ADD)
123   public void testSetCount_zeroToZero_addSupported() {
124     assertZeroToZero();
125   }
126 
127   @CollectionFeature.Require(SUPPORTS_REMOVE)
128   public void testSetCount_zeroToZero_removeSupported() {
129     assertZeroToZero();
130   }
131 
132   @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
133   public void testSetCount_zeroToZero_unsupported() {
134     try {
135       assertZeroToZero();
136     } catch (UnsupportedOperationException tolerated) {
137     }
138   }
139 
140   @CollectionSize.Require(absent = ZERO)
141   @CollectionFeature.Require(SUPPORTS_ADD)
142   public void testSetCount_oneToOne_addSupported() {
143     assertOneToOne();
144   }
145 
146   @CollectionSize.Require(absent = ZERO)
147   @CollectionFeature.Require(SUPPORTS_REMOVE)
148   public void testSetCount_oneToOne_removeSupported() {
149     assertOneToOne();
150   }
151 
152   @CollectionSize.Require(absent = ZERO)
153   @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
154   public void testSetCount_oneToOne_unsupported() {
155     try {
156       assertOneToOne();
157     } catch (UnsupportedOperationException tolerated) {
158     }
159   }
160 
161   @CollectionSize.Require(SEVERAL)
162   @CollectionFeature.Require(SUPPORTS_ADD)
163   public void testSetCount_threeToThree_addSupported() {
164     assertThreeToThree();
165   }
166 
167   @CollectionSize.Require(SEVERAL)
168   @CollectionFeature.Require(SUPPORTS_REMOVE)
169   public void testSetCount_threeToThree_removeSupported() {
170     assertThreeToThree();
171   }
172 
173   @CollectionSize.Require(SEVERAL)
174   @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
175   public void testSetCount_threeToThree_unsupported() {
176     try {
177       assertThreeToThree();
178     } catch (UnsupportedOperationException tolerated) {
179     }
180   }
181 
182   // Unconditional setCount size increases:
183 
184   @CollectionFeature.Require(SUPPORTS_ADD)
185   public void testSetCount_zeroToOne_supported() {
186     assertSetCount(samples.e3, 1);
187   }
188 
189   @CollectionFeature.Require({SUPPORTS_ADD,
190       FAILS_FAST_ON_CONCURRENT_MODIFICATION})
191   public void testSetCountZeroToOneConcurrentWithIteration() {
192     try {
193       Iterator<E> iterator = collection.iterator();
194       assertSetCount(samples.e3, 1);
195       iterator.next();
196       fail("Expected ConcurrentModificationException");
197     } catch (ConcurrentModificationException expected) {
198       // success
199     }
200   }
201 
202   @CollectionFeature.Require({SUPPORTS_ADD,
203       FAILS_FAST_ON_CONCURRENT_MODIFICATION})
204   public void testSetCountZeroToOneConcurrentWithEntrySetIteration() {
205     try {
206       Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
207       assertSetCount(samples.e3, 1);
208       iterator.next();
209       fail("Expected ConcurrentModificationException");
210     } catch (ConcurrentModificationException expected) {
211       // success
212     }
213   }
214 
215   @CollectionFeature.Require(SUPPORTS_ADD)
216   public void testSetCount_zeroToThree_supported() {
217     assertSetCount(samples.e3, 3);
218   }
219 
220   @CollectionSize.Require(absent = ZERO)
221   @CollectionFeature.Require(SUPPORTS_ADD)
222   public void testSetCount_oneToThree_supported() {
223     assertSetCount(samples.e0, 3);
224   }
225 
226   @CollectionFeature.Require(absent = SUPPORTS_ADD)
227   public void testSetCount_zeroToOne_unsupported() {
228     assertSetCountIncreasingFailure(samples.e3, 1);
229   }
230 
231   @CollectionFeature.Require(absent = SUPPORTS_ADD)
232   public void testSetCount_zeroToThree_unsupported() {
233     assertSetCountIncreasingFailure(samples.e3, 3);
234   }
235 
236   @CollectionSize.Require(absent = ZERO)
237   @CollectionFeature.Require(absent = SUPPORTS_ADD)
238   public void testSetCount_oneToThree_unsupported() {
239     assertSetCountIncreasingFailure(samples.e3, 3);
240   }
241 
242   // Unconditional setCount size decreases:
243 
244   @CollectionSize.Require(absent = ZERO)
245   @CollectionFeature.Require(SUPPORTS_REMOVE)
246   public void testSetCount_oneToZero_supported() {
247     assertSetCount(samples.e0, 0);
248   }
249 
250   @CollectionFeature.Require({SUPPORTS_REMOVE,
251       FAILS_FAST_ON_CONCURRENT_MODIFICATION})
252   @CollectionSize.Require(absent = ZERO)
253   public void testSetCountOneToZeroConcurrentWithIteration() {
254     try {
255       Iterator<E> iterator = collection.iterator();
256       assertSetCount(samples.e0, 0);
257       iterator.next();
258       fail("Expected ConcurrentModificationException");
259     } catch (ConcurrentModificationException expected) {
260       // success
261     }
262   }
263 
264   @CollectionFeature.Require({SUPPORTS_REMOVE,
265       FAILS_FAST_ON_CONCURRENT_MODIFICATION})
266   @CollectionSize.Require(absent = ZERO)
267   public void testSetCountOneToZeroConcurrentWithEntrySetIteration() {
268     try {
269       Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
270       assertSetCount(samples.e0, 0);
271       iterator.next();
272       fail("Expected ConcurrentModificationException");
273     } catch (ConcurrentModificationException expected) {
274       // success
275     }
276   }
277 
278   @CollectionSize.Require(SEVERAL)
279   @CollectionFeature.Require(SUPPORTS_REMOVE)
280   public void testSetCount_threeToZero_supported() {
281     initThreeCopies();
282     assertSetCount(samples.e0, 0);
283   }
284 
285   @CollectionSize.Require(SEVERAL)
286   @CollectionFeature.Require(SUPPORTS_REMOVE)
287   public void testSetCount_threeToOne_supported() {
288     initThreeCopies();
289     assertSetCount(samples.e0, 1);
290   }
291 
292   @CollectionSize.Require(absent = ZERO)
293   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
294   public void testSetCount_oneToZero_unsupported() {
295     assertSetCountDecreasingFailure(samples.e0, 0);
296   }
297 
298   @CollectionSize.Require(SEVERAL)
299   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
300   public void testSetCount_threeToZero_unsupported() {
301     initThreeCopies();
302     assertSetCountDecreasingFailure(samples.e0, 0);
303   }
304 
305   @CollectionSize.Require(SEVERAL)
306   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
307   public void testSetCount_threeToOne_unsupported() {
308     initThreeCopies();
309     assertSetCountDecreasingFailure(samples.e0, 1);
310   }
311 
312   // setCount with nulls:
313 
314   @CollectionSize.Require(absent = ZERO)
315   @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
316   public void testSetCount_removeNull_nullSupported() {
317     initCollectionWithNullElement();
318     assertSetCount(null, 0);
319   }
320 
321   @CollectionFeature.Require(value = {SUPPORTS_ADD, ALLOWS_NULL_VALUES},
322       absent = RESTRICTS_ELEMENTS)
323   public void testSetCount_addNull_nullSupported() {
324     assertSetCount(null, 1);
325   }
326 
327   @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES)
328   public void testSetCount_addNull_nullUnsupported() {
329     try {
330       setCountNoCheckReturnValue(null, 1);
331       fail("adding null with setCount() should throw NullPointerException");
332     } catch (NullPointerException expected) {
333     }
334   }
335 
336   @CollectionFeature.Require(ALLOWS_NULL_VALUES)
337   public void testSetCount_noOpNull_nullSupported() {
338     try {
339       assertSetCount(null, 0);
340     } catch (UnsupportedOperationException tolerated) {
341     }
342   }
343 
344   @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES)
345   public void testSetCount_noOpNull_nullUnsupported() {
346     try {
347       assertSetCount(null, 0);
348     } catch (NullPointerException tolerated) {
349     } catch (UnsupportedOperationException tolerated) {
350     }
351   }
352 
353   @CollectionSize.Require(absent = ZERO)
354   @CollectionFeature.Require(ALLOWS_NULL_VALUES)
355   public void testSetCount_existingNoNopNull_nullSupported() {
356     initCollectionWithNullElement();
357     try {
358       assertSetCount(null, 1);
359     } catch (UnsupportedOperationException tolerated) {
360     }
361   }
362 
363   // Negative count.
364 
365   @CollectionFeature.Require(SUPPORTS_REMOVE)
366   public void testSetCount_negative_removeSupported() {
367     try {
368       setCountNoCheckReturnValue(samples.e3, -1);
369       fail("calling setCount() with a negative count should throw "
370           + "IllegalArgumentException");
371     } catch (IllegalArgumentException expected) {
372     }
373   }
374 
375   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
376   public void testSetCount_negative_removeUnsupported() {
377     try {
378       setCountNoCheckReturnValue(samples.e3, -1);
379       fail("calling setCount() with a negative count should throw "
380           + "IllegalArgumentException or UnsupportedOperationException");
381     } catch (IllegalArgumentException expected) {
382     } catch (UnsupportedOperationException expected) {
383     }
384   }
385 
386   // TODO: test adding element of wrong type
387 }
388